#include <func.h>
#include <stdio.h>
#include "Command.h"
#include "Network.h"
#include "Configuration.h"

extern Configuration conf;
extern UserInfo info;

void* send_chunk(void* arg)
{
    Chunk* chunk = (Chunk*) arg;

    const char* host = configuration_get(&conf, "HOST");
    const char* port = configuration_get(&conf, "PORT");
    int connfd = tcp_connect(host, port);

    // 1. 构建 Request 对象: sequence, chunk大小, sha256sum, 服务器存储路径, token
    Request req;
    bzero(&req, sizeof(req));
    req.type = REQ_UPLOAD_CHUNK;

    char sha256sum[65] = "";
    sha256_chunk(chunk, sha256sum);
    sprintf(req.value, "%d %ld %s %s %s",
            chunk->sequence,
            chunk->size, 
            sha256sum, 
            chunk->path, 
            info.token);
    printf("DEBUG: req.value = %s\n", req.value);
    
    req.length = strlen(req.value);
    // 2. 发送 Request 请求
    int length = 2 * sizeof(int) + req.length;
    send(connfd, &length, sizeof(int), MSG_WAITALL);
    send(connfd, &req, length, MSG_WAITALL);

    // 3. 接收 Response 
    Response resp;
    bzero(&resp, sizeof(resp));
    recv(connfd, &length, sizeof(int), MSG_WAITALL);
    recv(connfd, &resp, length, MSG_WAITALL);
    
    switch (resp.status) {
    case RESP_NOT_AUTH:
        login_or_register(connfd);
        break;
    case RESP_EXIST:
        break;
    case RESP_NOEXIST:
        off_t offset = chunk->sequence * CHUNK_SIZE; 
        size_t bytes = 0;   // 发送的字节数目
        while (bytes < chunk->size) {
            int n = sendfile(connfd, chunk->fd, &offset, chunk->size - bytes);
            bytes += n;
        }
        break;
    }
    // 断开连接
    close(connfd);
    return NULL;
}

int send_chunked_file(int fd, const char* path, long fsize)
{
    int n = (fsize + CHUNK_SIZE - 1) / CHUNK_SIZE;
    // 创建 n 个线程，利用客户端的并行能力
    pthread_t threads[n];
    Chunk chunks[n];

    for (int i = 0; i < n; i++) {
        chunks[i].sequence = i;
        chunks[i].fd = fd;
        chunks[i].size = (fsize - chunks[i].sequence * CHUNK_SIZE >= CHUNK_SIZE) ?
            CHUNK_SIZE : (fsize - chunks[i].sequence * CHUNK_SIZE);
        chunks[i].path = path;

        // 创建新的连接
        pthread_create(&threads[i], NULL, send_chunk, &chunks[i]);
    }

    // 主线程等待所有子线程结束
    for (int i = 0; i < n; i++) {
        pthread_join(threads[i], NULL);
    }
    return 0;
}

void upload_file(int connfd, const char* file)
{
    int fd = open(file, O_RDONLY);
    struct stat fstatus;
    fstat(fd, &fstatus);
    long fsize = fstatus.st_size;

    // 构建服务器路径
    char abs_path[PATH_MAX];
    path_absolute(&info.cwd, file, abs_path);
    printf("DEBUG: abs_path = %s\n", abs_path);

    char server_path[PATH_MAX];   
    sprintf(server_path, "/%s%s", info.username, abs_path);
    printf("DEBUG: server_path = %s\n", server_path);
     
    if (fsize >= 2 * CHUNK_SIZE) {
        send_chunked_file(fd, server_path, fsize);
        close(fd);
        return ;
    }

    Request req;
    req.type = REQ_UPLOAD_FILE;
    // 文件大小, sha256sum, 服务器端存储的绝对路径
    
    char cmd[1024];
    sprintf(cmd, "sha256sum %s", file);
    FILE* pipe = popen(cmd, "r");

    char sha256sum[65] = {'\0'};
    fread(sha256sum, 1, 64, pipe);

    // 服务器端存储的路径：/username/用户的当前工作目录/filename
    // char server_path[1024] = "/xia/dir/a.txt";
    sprintf(req.value, "%ld %s %s", fsize, sha256sum, server_path);

    req.length = strlen(req.value);

    int length = 2 * sizeof(int) + req.length;
    send(connfd, &length, sizeof(int), MSG_WAITALL);
    send(connfd, &req, length, MSG_WAITALL);

    Response resp;
    recv(connfd, &length, sizeof(int), MSG_WAITALL);
    recv(connfd, &resp, length, MSG_WAITALL);

    switch (resp.status) {
    case RESP_EXIST:
        close(fd);
        break;
    case RESP_NOEXIST:
        // 发送文件内容
        char buf[4096]={0};
        printf("send file success\n");
        int n;
        while ((n = read(fd, buf, 4096)) > 0) {
            send(connfd, buf, n, MSG_WAITALL);
        }
        close(fd);
        break;
    }
}
